package com.ztmg.common.util;

import com.github.pagehelper.Page;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Copyright:   Copyright 2016 - 2016 Meyli. Co. Ltd. All Rights Reserved.
 * Date:        2016年4月8日 上午9:41:38
 * Author:      gaobo
 * Version:     V1.0.0.1
 * Description: 实体工具类
 */
public class BeanTools {
    private static final Logger logger = LoggerFactory.getLogger(BeanTools.class);

    private BeanTools() {
    }

    /**
     * 通用toString方法
     *
     * @param bean Object
     * @return String
     */
    public static String beanToString(Object bean) {

        Class<?> c = bean.getClass();
        Field[] fields = c.getDeclaredFields();

        StringBuilder buffer = new StringBuilder();
        buffer.append(
                c.getName().substring(c.getName().lastIndexOf(".") + 1) + '[');
        try {
            AccessibleObject.setAccessible(fields, true);

            for (int i = 0; i < fields.length; i++) {
                Field f = fields[i];
                buffer.append(f.getName());
                buffer.append('=');
                Object value = f.get(bean);
                if (value instanceof Object[]) {
                    Object[] obj = (Object[]) value;
                    buffer.append(arrayToString(obj));
                } else {
                    buffer.append(fieldHandler(f, value));
                }
                if (i + 1 < fields.length) {
                    buffer.append(',');
                }
            }
        } catch (Exception e) {
            // 异常不往外仍，记录debug级别，不影响业务
        }
        buffer.append(']');
        return buffer.toString();
    }

    /**
     * 对象数组toString
     *
     * @param objs Object[] 对象数组
     * @return String 对象数组的字符串表达
     */
    public static String arrayToString(Object[] objs) {
        if (null != objs) {
            StringBuilder objsBuffer = new StringBuilder();
            objsBuffer.append("{");
            for (int i = 0; i < objs.length; i++) {
                objsBuffer.append((null == objs[i]) ? "null" : objs[i]);
                if (i < objs.length - 1) {
                    objsBuffer.append(",");
                }
            }
            objsBuffer.append("}");
            return objsBuffer.toString();
        }
        return "{null}";
    }

    /**
     * @param obj
     * @return
     * @Title: transBean2Map
     * @Description: 将实体javabean类转Map<String                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               Object>类
     * @return: Map<String                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               Object>
     * @Author: yangshuai
     * @date: 2015年4月21日 下午8:02:13
     */
    public static Map<String, Object> transBean2Map(Object obj,
                                                    boolean filterNullFlag) {

        if (obj == null) {
            return new HashMap<String, Object>(10);
        }
        Map<String, Object> model = new HashMap<String, Object>(10);
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] propertyDescriptors =
                    beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();

                // 过滤class属性
                if (!"class".equals(key)) {
                    // 得到property对应的getter方法
                    Method getter = property.getReadMethod();
                    Object value = getter.invoke(obj);
                    transBean2Map(filterNullFlag, model, key, value);
                }
            }
        } catch (Exception e) {
            // 异常不往外仍，记录debug级别，不影响业务
        }
        return model;
    }

    private static void transBean2Map(boolean filterNullFlag,
                                      Map<String, Object> model, String key, Object value) {
        if (filterNullFlag) {
            if (isNotBlank(value)) {
                model.put(key, value);
            }
        } else {
            model.put(key, value);
        }
    }

    /**
     * @param oldObj 转换后的返回对象
     * @param newObj 需要转换的对象
     * @throws Exception
     * @Title: copyProperty
     * @Description: 对象转换（对象属性拷贝）
     * @return: void
     * @Author: qhxiaoa
     * @date: 2015年8月3日 下午6:01:40
     */
    @SuppressWarnings("rawtypes")
    public static void copyProperty(Object oldObj, Object newObj)
            throws Exception {
        // 新的class
        Class newClass = newObj.getClass();
        // 老的class
        Class oldClass = oldObj.getClass();
        // 该类所有的属性
        Field[] newFields = newClass.getDeclaredFields();
        // 新的属性
        Field newField = null;
        // 老的属性
        Field oldField = null;
        for (Field f : newFields) {
            // 类中的属性名称
            String fieldName = f.getName();
            if (!"serialVersionUID".equals(fieldName)) {
                // 通过属性名称获取属性
                newField = newClass.getDeclaredField(fieldName);
                // 获取属性的值时需要设置为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
                // 值为 false 则指示反射的对象应该实施 Java 语言访问检查。
                newField.setAccessible(true);
                // 根据属性获取对象上的值
                Object newObject = newField.get(newObj);
                // 过滤空的属性或者一些默认值
                if (!isContinue(newObject)) {
                    oldField = oldClass.getDeclaredField(fieldName);
                    oldField.setAccessible(true);
                    oldField.set(oldObj, newObject);
                }

            }
        }
    }

    /**
     * 查询数据实体转换为VO
     *
     * @param domainObj
     * @param voObj
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    public static void domainToVo(Object domainObj, Object voObj)
            throws Exception {
        // 新的class
        Class newClass = voObj.getClass();
        // 老的class
        Class oldClass = domainObj.getClass();
        // 该类所有的属性
        Field[] newFields = newClass.getDeclaredFields();
        // 新的属性
        Field newField = null;
        // 老的属性
        Field oldField = null;
        for (Field f : newFields) {
            // 类中的属性名称
            String fieldName = f.getName();
            if (!"serialVersionUID".equals(fieldName)) {

                // 根据属性获取对象上的值
                oldField = oldClass.getDeclaredField(fieldName);
                oldField.setAccessible(true);
                Object oldObject = oldField.get(domainObj);
                // 过滤空的属性或者一些默认值
                if (!isContinue(oldObject)) {
                    // 通过属性名称获取属性
                    newField = newClass.getDeclaredField(fieldName);
                    // 获取属性的值时需要设置为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
                    // 值为 false 则指示反射的对象应该实施 Java 语言访问检查。
                    newField.setAccessible(true);
                    newField.set(voObj, oldObject);
                }

            }
        }
    }

    /**
     * @param model     实体
     * @param attribute 属性值
     * @return
     * @throws Exception
     * @Title: getAttributeValue
     * @Description: 获取类中属性的值
     * @return: String
     * @Author: bogaoe
     * @date: 2015年11月2日 下午3:54:17
     */
    public static String getAttributeValue(Object model, String attribute)
            throws Exception {
        String result = "";
        Field[] field = model.getClass().getDeclaredFields(); // 获取实体类的所有属性，返回Field数组
        for (int j = 0; j < field.length; j++) { // 遍历所有属性
            String name = field[j].getName(); // 获取属性的名字
            if (name.equals(attribute)) {
                name = name.substring(0, 1).toUpperCase() + name.substring(1); // 将属性的首字符大写，方便构造get，set方法
                String type = field[j].getGenericType().toString(); // 获取属性的类型
                Method m = model.getClass().getMethod("get" + name);
                result = getSimpleTypeValue(type, model, m);
            }
        }
        return result;
    }

    public static String printHtml(Object model, String html) throws Exception {
        String regex = "\\{.*?\\}";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(html);
        String result = html;
        while (matcher.find()) {
            String attr = matcher.group().replaceAll("\\{|\\}", "");
            String newAttr = BeanTools.getAttributeValue(model, attr);
            result = result.replaceAll("\\{" + attr + "\\}", newAttr);
        }
        return result;
    }

    /**
     * 是否跳出本次循环
     *
     * @param object
     * @return true 是 有null或者默认值 false 否 有默认值
     * @author 2014-11-6 上午11:37:22
     */
    private static boolean isContinue(Object object) {
        if (object == null || "".equals(object)) {
            return true;
        }
        String valueStr = object.toString();
        if ("0".equals(valueStr) || "0.0".equals(valueStr)) {
            return true;
        }
        return false;
    }

    /**
     * @param field
     * @param value
     * @return
     * @Title: fieldHandler
     * @Description: 安全特殊属性值过滤
     * @return: Object
     * @Author: boliur
     * @date: 2015年12月10日 下午3:51:58
     */
    private static Object fieldHandler(Field field, Object value) {
        if ("password".equals(StringUtils.lowerCase(field.getName()))
                || "pwd".equals(StringUtils.lowerCase(field.getName()))
                || "newpswd".equals(StringUtils.lowerCase(field.getName()))
                || "oldpswd".equals(StringUtils.lowerCase(field.getName()))) {
            return "******";
        } else {
            return value;
        }
    }

    /**
     * @param obj
     * @return
     * @Title: isNotBlank
     * @Description: object是否为null或者空字符串
     * @return: boolean
     * @Author: boliur
     * @date: 2015年11月5日 下午4:49:51
     */
    private static boolean isNotBlank(Object obj) {
        if (null == obj) {
            return false;
        }
        if (obj instanceof String) {
            return StringUtils.isNotBlank(obj.toString());
        }
        return true;
    }

    /**
     * @param type
     * @param model
     * @param m
     * @return
     * @throws Exception
     * @Title: getSimpleTypeValue
     * @Description: 获取简单数据类型的值
     * @return: String
     * @Author: boliur
     * @date: 2015年11月5日 下午5:45:53
     */
    private static String getSimpleTypeValue(String type, Object model,
                                             Method m) throws Exception {
        String result = "";
        Object obj = m.invoke(model);
        if (null != obj) {
            switch (type.hashCode()) {
                case 673016845:// "class java.lang.String".hashCode()
                case -1066470206:// "class java.lang.Integer".hashCode():
                case 104431:// "int".hashCode():
                case 575539456:// "class java.lang.Short".hashCode():
                case 239044557:// "class java.lang.Double".hashCode():
                case 1335156652:// "class java.lang.Boolean".hashCode():
                case -1228813654:// "class java.util.Date".hashCode():
                    result = obj.toString();
                    break;
                default:
                    break;
            }
        }
        return result;
    }

    /**
     * 将一个 Map 对象转化为一个 JavaBean
     *
     * @param type 要转化的类型
     * @param map  包含属性值的 map
     * @return 转化出来的 JavaBean 对象
     * @throws IntrospectionException    如果分析类属性失败
     * @throws IllegalAccessException    如果实例化 JavaBean 失败
     * @throws InstantiationException    如果实例化 JavaBean 失败
     * @throws InvocationTargetException 如果调用属性的 setter 方法失败
     */
    public static Object convertMap(Class type, Map map)
            throws IntrospectionException, IllegalAccessException,
            InstantiationException, InvocationTargetException {
        BeanInfo beanInfo = Introspector.getBeanInfo(type); // 获取类属性 
        Object obj = type.newInstance(); // 创建 JavaBean 对象 

        // 给 JavaBean 对象的属性赋值 
        PropertyDescriptor[] propertyDescriptors =
                beanInfo.getPropertyDescriptors();
        for (int i = 0; i < propertyDescriptors.length; i++) {
            PropertyDescriptor descriptor = propertyDescriptors[i];
            String propertyName = descriptor.getName();

            if (map.containsKey(propertyName)) {
                // 下面一句可以 try 起来，这样当一个属性赋值失败的时候就不会影响其他属性赋值。 
                Object value = map.get(propertyName);

                Object[] args = new Object[1];
                args[0] = value;

                descriptor.getWriteMethod().invoke(obj, args);
            }
        }
        return obj;
    }

    /**
     * Description:map转为实体
     *
     * @param beanName
     * @param map
     * @return
     * @throws Exception Date:        2016年5月6日 下午2:44:14
     *                   Author:      gaobing
     */
    @SuppressWarnings({"unused", "unchecked", "rawtypes"})
    public static Object map2Bean(String beanName, Map map) throws Exception {
        Class clazz = Class.forName(beanName);
        //实例化类  
        Object entity = clazz.newInstance();
        Set<String> keys = map.keySet();
        //变量map 赋值  
        for (String key : keys) {
            String fieldName = key;
            //判断是sql 还是hql返回的结果  
            if (key.equals(key.toUpperCase())) {
                //获取所有域变量  
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    if (field.getName().toUpperCase().equals(key))
                        fieldName = field.getName();
                    break;
                }
            }
            //设置赋值  
            try {
                //参数的类型  clazz.getField(fieldName)  
                Class<?> paramClass =
                        clazz.getDeclaredField(fieldName).getType();
                //拼装set方法名称  
                String methodName =
                        "set" + fieldName.substring(0, 1).toUpperCase()
                                + fieldName.substring(1);
                //根据名称获取方法  
                Method method = clazz.getMethod(methodName, paramClass);
                //调用invoke执行赋值  
                method.invoke(entity, map.get(key));
            } catch (Exception e) {
                //NoSuchMethod  
            }
        }
        return entity;
    }

    /**
     * Description:map转bean  支持多种数据类型
     *
     * @param map
     * @param cls
     * @return
     * @throws Exception Date:        2016年5月12日 下午4:28:15
     *                   Author:      gaobing
     */
    public static Object transferMap2Bean(Map map, Class cls) throws Exception {
        Object obj = null;
        try {
            obj = cls.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 取出bean里的所有方法
        Method[] methods = cls.getMethods();
        for (int i = 0; i < methods.length; i++) {
            // 取方法名
            String method = methods[i].getName();
            // 取出方法的类型
            Class[] cc = methods[i].getParameterTypes();
            if (cc.length != 1) {
                continue;
            }
            // 如果方法名没有以set开头的则退出本次for
            if (method.indexOf("set") < 0) {
                continue;
            }
            // 类型
            String type = cc[0].getSimpleName();
            try {
                // 转成小写
                // Object value = method.substring(3).toLowerCase();
                Object value = method.substring(3, 4).toLowerCase() + method.substring(4);
                //System.out.println("value == " + value);
                // 如果map里有该key
                if (map.containsKey(value) && map.get(value) != null) {
                    // 调用其底层方法
                    setValue(type, map.get(value), i, methods, obj);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return obj;
    }

    /**
     * 调用底层方法设置值
     */
    private static void setValue(String type, Object value, int i,
                                 Method[] method, Object bean) {
        if (value != null && !value.equals("")) {
            try {
                if (type.equals("String")) {
                    // 第一个参数:从中调用基础方法的对象 第二个参数:用于方法调用的参数
                    method[i].invoke(bean, new Object[]{value});
                } else if (type.equals("int") || type.equals("Integer")) {
                    method[i].invoke(bean,
                            new Object[]{new Integer("" + value)});
                } else if (type.equals("double") || type.equals("Double")) {
                    method[i].invoke(bean,
                            new Object[]{new Double("" + value)});
                } else if (type.equals("float") || type.equals("Float")) {
                    method[i].invoke(bean,
                            new Object[]{new Float("" + value)});
                } else if (type.equals("long") || type.equals("Long")) {
                    method[i].invoke(bean,
                            new Object[]{new Long("" + value)});
                } else if (type.equals("boolean") || type.equals("Boolean")) {
                    method[i].invoke(bean,
                            new Object[]{Boolean.valueOf("" + value)});
                } else if (type.equals("BigDecimal")) {
                    method[i].invoke(bean,
                            new Object[]{new BigDecimal("" + value)});
                } else if (type.equals("Date")) {
                    Date date = null;
                    if (value.getClass().getName().equals("java.util.Date")) {
                        date = (Date) value;
                    } else {
                        String format = ((String) value).indexOf(":") > 0
                                ? "yyyy-MM-dd hh:mm:ss" : "yyyy-MM-dd";
                        SimpleDateFormat sf = new SimpleDateFormat();
                        sf.applyPattern(format);
                        date = sf.parse((String) (value));
                    }
                    if (date != null) {
                        method[i].invoke(bean, new Object[]{date});
                    }
                } else if (type.equals("byte[]")) {
                    method[i].invoke(bean,
                            new Object[]{new String(value + "").getBytes()});
                }
            } catch (Exception e) {
                System.out
                        .println("将linkHashMap 或 HashTable 里的值填充到javabean时出错,请检查!");
                e.printStackTrace();
            }
        }
    }

    /**
     * 将集合转换为另一集合,并保留分页信息
     *
     * @param sourceList       原集合
     * @param destinationClass 转换后集合内元素class
     * @param <T>
     * @return
     */
    public static <T> List<T> mapPage(Collection sourceList, Class<T> destinationClass) {
        if (sourceList == null || sourceList.isEmpty() || destinationClass == null) {
            return Collections.emptyList();
        }
        List<T> destinationList = Lists.newArrayList();
        sourceList.parallelStream().forEach(sourceObject -> {
            try {
                T destinationObject = destinationClass.newInstance();
                BeanUtils.copyProperties(sourceObject, destinationObject);
                destinationList.add(destinationObject);
            } catch (InstantiationException | IllegalAccessException e) {
                logger.error(e.getMessage());
            }
        });
        if (sourceList instanceof Page) {
            Page sourcePage = (Page) sourceList;
            sourcePage.clear();
            sourcePage.addAll(destinationList);
            return sourcePage;
        }
        return destinationList;
    }


    public static void main(String[] args) throws Exception {

    }
}
